package com.diy.sigmund.diycommon.util.encrypt;

import java.util.Random;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * AES 128bit 加密解密工具类
 * https://blog.csdn.net/qq_42402854/article/details/89307480
 *
 * 浅谈常见的七种加密算法及实现
 * https://blog.csdn.net/baidu_22254181/article/details/82594072
 *
 * @author ylm-sigmund
 * @since 2021/8/17 21:06
 */
public class AesEncryptUtil {

    /** 加解密算法 */
    private static final String KEY_ALGORITHM = "AES";
    /** 算法/模式/补码方式 */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/NoPadding";
    /** 秘钥 */
    private static final String KEY = "0000000304215281";
    /** 偏移量 */
    private static final String IV = "2u9sn1kenwhukazv";
    private static final Logger log = LoggerFactory.getLogger(AesEncryptUtil.class);

    /** 封装加密方法 */
    public static String encrypt(String data) throws Exception {
        return encrypt(data, KEY, IV);
    }

    /** 封装解密方法 */
    public static String decrypt(String data) throws Exception {
        return decrypt(data, KEY, IV);
    }

    /**
     * 加密方法
     * 使用AES-128-CBC加密模式，key需要为16位,key和iv可以相同！
     *
     * @param data 明文
     * @param key  秘钥
     * @param iv   偏移量 长度16
     * @return 密文
     * @throws Exception Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        int blockSize = cipher.getBlockSize();

        byte[] dataBytes = data.getBytes();
        int plaintextLength = dataBytes.length;
        if (plaintextLength % blockSize != 0) {
            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
        }

        byte[] plaintext = new byte[plaintextLength];
        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());

        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        // 加密
        byte[] encrypted = cipher.doFinal(plaintext);
        //通过Base64转码返回
        return new Base64().encodeToString(encrypted);
    }

    /**
     * 解密方法
     *
     * @param data 密文
     * @param key  秘钥
     * @param iv   偏移量 长度16
     * @return 明文
     * @throws Exception Exception
     */
    public static String decrypt(String data, String key, String iv) throws Exception {
        byte[] encrypted1 = new Base64().decode(data);

        Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
        //使用密钥初始化，设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        //执行操作
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original);
        return originalString;
    }

    /**
     * 用于生成一组16位随机数 key
     *
     * @return String
     */
    public static String getRandomStringKey() {
        int hashCodeValue = UUID.randomUUID().hashCode();
        if (hashCodeValue < 0) {
            hashCodeValue = -hashCodeValue;
        }
        // 左边补0,16位，进制（d,x）
        return String.format("%016d", hashCodeValue);
    }

    /**
     * 用于生成16位的随机数 iv
     *
     * @return String
     */
    public static String getRandomStringIv() {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer key = new StringBuffer();
        for (int i = 0; i < 16; i++) {
            int keyNumber = random.nextInt(base.length());
            key.append(base.charAt(keyNumber));
        }
        return key.toString();
    }


    /**
     * 测试
     */
    public static void main(String[] args) throws Exception {
        String data = "admin123";
        String key = getRandomStringKey();
        String iv = getRandomStringIv();

        String enData = encrypt(data, key, iv);

        System.out.println(key);
        System.out.println(iv);
        System.out.println(enData);
        System.out.println(decrypt(enData, key, iv));
    }
}
